/************************************************************************
FileIO.H
 - Originally written by Bartlett Technologies 4/4/2001
 - Revised by Chon Tang.   6/14/2001.
		Description removed; comments added; MBR added.
 - Revised, 8/11/2001.
      fs_fremove added.
 - Revised, 5/3/2002.
      added 186/386 DMA support.
 - Revised, 5/12/2002.
 	   fs_fflush added.
 - Revised, 5/15/2002
 		fs_fseek added.
 - Revised, 5/22/2002
 		fs_setFlag added, NO_DMA support.
 - Revised, 10/22/2002
      fs_fputcluster added.
 - Revised, 3/1/2003
      fs_frdump added.
 - Revised, 4/22/2003
      fs_fwdump added.
 - Revised, 8/15/2003
 		fs_callocate added.
 - Revised, 5/15/2005
      updated fs_fopen_callocate added.
 - Revised, 8/25/2005
      fs_change_dir, fs_create_dir added.

 TERN, Inc.
 All rights reserved.

 NOTES:

 This is a single-threaded library supporting a basic FAT12/FAT16 (without LFN,
 extended filenames) filesystem.  Only a root directory is currently supported.
 Simple functions for reading, writing, and appending files are available.

 filegeo.h contains additional functions that can be used to manage and look
 at the filesystem at a lower cluster-based level.

 This library requires use of dynamic memory and the heap; make sure you
 have 'heapsize.c' added to your project (see TERN documentation on dynamic
 memory allocation for details).

  DMA SUPPORT:  (New 1/23/2003)
  The new filesystem library uses DMA0 (for 186/386), which is shared with SER0.
  If you are using SER0 within your code (by calling s0_init, for example), you
  will need to turn off DMA support in the filesystem library.

  See the FAQ in the readme.txt for the filesystem projects for more details.
  Or, contact tech@tern.com.
********************** DISCLAIMER *********************************
This library is not to be used in situations where improper operation
or improper use could cause loss of life, personal injury or property damage.
***********************************************************************/
#ifndef _FILEIO_H_
#define _FILEIO_H_

#define FNLEN 11  // 8.3 filename length, 8+3, '.' is assumed.

/* File Operation: ERROR CODES */
#define fOK           0	 // No errors reported.
#define fEND          1  // End of directory reached.
#define fERR          2  // Error encountered working with file.
#define fDISKFULL     3  // Disk full.
#define fWRITE_ERROR  4  // A low-level write operation failed.
#define fREAD_ERROR   5  // A low-level Read operation failed.
#define fCORRUPTED    6  // An empty cluster was found in FAT chain.
#define fDIRFULL      7  // Directory entries on this disk.
#define fINVALID      8  // An invalid call to findfirst or findnext.
#define fEOF          9  // End of file reached while reading.
#define fILLEGAL 	 0x0A  // Tried to read a "w" file or write a "r" file.
#define fMEMORY    0x0B  // An operation failed to secure enough memory.

/* These flags are used by StampTimeHMSMDY(). */
#define TDCREATED       'c'  // Flag to alter 'create' date and time.
#define TDMODIFIED      'm'  // Flag to alter 'modified' date and time.
#define TDACCESSED      'a'  // Flag to alter 'accessed' date.

/* File attribute definitions.  Stored in fs_descrip.ff_attrib.
   These flags can be anded, so that 0x22 means archived and hidden. */
#define FA_ARCH  0x20  // Archive bit.
#define FA_HIDDEN  0x02 // Hidden file bit, this code displays hidden files.
#define FA_SYSTEM  0x04
#define FA_RDONLY  0x01 // Read Only bit, file cannot be written or appended.
#define FA_CLEAR  0x0 // No attributes are set.
#define FA_LABEL  0x08 // Volume label.
#define FA_DIREC  0x10 // Directory.
#define FA_EXTEND 0x0f // Extended/LFN attributes, ignored by this code.

struct fs_descrip; /* File descriptor/handle for an open file */

/*****************************************************************
int fs_initPCFlash(void);

DESCRIPTION:
	This function is called before any other disk operations.
	It should be called again if a new Card is inserted.

RETURNS:
   0,     if a card w/ FAT filesystem is located & initialized.
   other, if card was 'busy' (not found), or if disk geometry is not
       	    correct.  Check <filegeo.h> for error-code description.
*****************************************************************/
int fs_initPCFlash(void);

/*****************************************************************
unsigned int fs_availableSpace(void);

DESCRIPTION:
	This function does a simple, but also slow, check to determine
   the number of 'empty' clusters remaining.  Don't call this
   frequently, since it does take some seconds to return (depends on
   size of card).

RETURNS:
	Number of clusters available in current card.
*****************************************************************/
unsigned int fs_availableSpace(void);

/*****************************************************************
int fs_setFlags(unsigned int);

DESCRIPTION:
	Sets option flags in the filesys library.  The list of
   available options will change over time.

RETURNS:
   0,          if the change succeeds.
*****************************************************************/
#define  FS_NO_DMA0  0x0001    // Set this flag if you do not wish DMA0 to be used.
#define  FS_ONE_FAT  0x0002    // Set this flag to keep only one copy of the FAT for
                               //    write efficiency on close.  See README.
int fs_setFlags(unsigned int fs_cmds);

/****************************************************************
struct fs_descrip *fs_fopen(const char *filename, int flags)

DESCRIPTION:
   Opens and prepares a file for operation.

   The flag should be one of the following values:
     O_RDONLY : (open file for read only, fails if file doesn't exist),
     O_WRONLY : (open/creates a file, fails if file exists),
     O_APPEND : (open a file and prepares to append, positioning file pointer
                 at the end of the program)

Updated: 2/10/03
   Write flags have been changed.  O_READ, O_WRITE replace the ONLY flags.
   O_WRITE:  Creates a NEW empty file if possible (implies O_READ).
   O_APPEND: Open EXISTING file for writing (also implies O_READ).
   O_READ:   Opens an existing file for read only.

   These can be combined for other behavior:
       O_READ | O_WRITE   :        Open a new file; if it exists, open to read.
       O_WRITE | O_APPEND :        Open a new file; if it exists, open to append.

RETURNS:
   A 'struct fs_descrip' handle to the open file, or NULL if it fails.
Updated: 6/9/03
   Always returns an allocated descriptor, unless out of memory or the
   arguments are incorrect.  It is NOT enough to check 'NULL' to see whether
   an open has failed... the ff_status flag should be checked to determine if
   an error occured during open.

NOTE:
   After a file is opened successfully (or unsuccessfully), it should always
   be *closed* with fs_fclose() in order to free the memory associated with the
   file handle (even if an error occured while accessing the file), and to
   finalize any modifications to the file.
*****************************************************************/
#define O_RDONLY     0x1
#define O_WRONLY     0x2
#define O_APPEND     0x4

/* These renamed to indicate READ/WRITE not exclusive. */
#define O_READ       0x1
#define O_WRITE      0x2
struct fs_descrip *fs_fopen(const char *filename, int flags);

/*************************************************************
int fs_change_dir(const char* dir_name);

DESCRIPTION
Your application will always start with the root as "active"
directory, and you should initialize as normally.  Any file
open operations will still occur in the root directory.

A subsequent call to fs_change_dir() can be used to change
the current "active" directory to one of the available
sub-directories.  Any following file open operations will
take place in the new sub-directory.  (The standard DOS
'..' directory, for returning to parent directory, should
be available.)

Note that you can have files from multiple directories
open simultaneously.  Any updates, closes, writes, reads,
etc... will still work correctly on an open struct fs_descrip*,
even if the directory has changed.

RETURNS:
- If the operation succeeds, fOK is returned.
- If the subdirectory doesn't seem to exist, fILLEGAL is
returned.  Other errors, and a standard fXXXX error code
from fileio.h is returned.
***********************************************************/
int fs_change_dir(const char* dirname);

/*************************************************************
int fs_create_dir(const char* dir_name);

DESCRIPTION
Creates a sub-directory in the current directory with 'dir_name'.
'dir_name' must be 8 characters or less in length, as is standard
in basic 8.3 naming convention.

This instruction is equivalent to opening a file, meaning that there
must also be sufficient heap space for a cluster worth of memory to be
allocated.  This cluster of memory is automatically freed upon return,
and no further handling is needed, except the checking of return value
for success.

RETURNS:
- If the operation succeeds, fOK is returned.
- If the operation fails, standard fXXXX error code from fileio.h
is returned (very similar in error semantics to fs_fopen).
***********************************************************/
int fs_create_dir(const char* dirname);

/*****************************************************************
BETA -  This function only useful for marking 'timestamp' on a directory
being created.
******************************************************************/
int fs_get_dir(const char* dirname, struct fs_descrip* dir_blk);

/****************************************************************
int fs_findfirst(char *pathname, struct fs_descrip *descrip)

DESCRIPTION:
   Finds the *first* file entry (including directories and 'labels'
   corresponding to the argument 'pathname'.  The handle for the file is
   returned in the 'descrip' argument (you must allocate memory for it
   before making the call).

   This file is not* actually opened (you don't need to fs_fclose() it later,
   either).

RETURNS:
   fOK        : a file was found.
   fEND       : The end of the directory specified in 'pathname' was reached,
                but no file found.
	error code : Check this file and <filegeo.h> for error-code descriptions.

NOTE:
	For version 1.0, pathname MUST be "*.*".  Any other pathname will generate
   an error.  In later versions, other pathnames/wildcards may be supported.
   So, a call to fs_findfirst("*.*", .....) returns the first file entry
   in the root directory.
*****************************************************************/
int fs_findfirst(char *pathname, struct fs_descrip *descrip);

/****************************************************************
int fs_findnext(struct fs_descrip *fs_descrip);

DESCRIPTION:
   Given a file descriptor, find the next entry in the file's directory.  The
   details of the file are written into the same argument file descriptor.

   As before, the file is *not* actually opened.

RETURNS:
   fOK        : a file was found.
   fEND       : The end of the directory specified in 'pathname' was reached,
   				 but no file found.
	error code : Check this file and <filegeo.h> for error-code descriptions.

NOTE:
	Use this function, in combination with fs_findfirst, to iterate through the
   entries in a directory.
*****************************************************************/
int fs_findnext(struct fs_descrip *fs_descrip);

/****************************************************************
unsigned char fs_fgetc(struct fs_descrip *fs_descrip)

DESCRIPTION:
	Get a single byte from the opened file pointed to by the file descriptor.

RETURNS:
   Normally, next byte of data.

   '\0'     : Default return value if a read from file is not possible.
              In this case, check fs_descrip->ff_status to determine the cause.
              Might be fEOF (end of file), fILLEGAL (illegal operation),
              or other error code.  (fOK indicates the read value was '\0').
*****************************************************************/
unsigned char fs_fgetc(struct fs_descrip *fs_descrip);

/****************************************************************
unsigned char fs_fputc(const unsigned char s, struct fs_descrip *fs_descrip)

DESCRIPTION:
	Writes a single byte to the opened file pointed to by the file descriptor.

RETURNS:
   Normally, the character that was just written to the file.

   '\0'     : Default return value if a write to file is not possible.
              In this case, check fs_descrip->ff_status to determine the cause.
              Might be fEOF (end of file), fILLEGAL (illegal operation),
              or other error code (full disk)... or fOK if the value written
              was a null.
*****************************************************************/
unsigned char fs_fputc(const unsigned char s, struct fs_descrip *fs_descrip);

/****************************************************************
unsigned char fs_fputcluster(unsigned char* pClu, struct fs_descrip *fs_descrip)

DESCRIPTION:
	Writes an entire buffer to the compact flash card, as a cluster, for
   improved performance.

   _pClu should point to a memory buffer of the same size as a cluster,
   which can be determined by looking in the field g_ClusterSize in the
   File geometry.  (Look at fs_getGeometry() for details.)

RETURNS:
   0,   if error occured.
   1,   if successful.
*****************************************************************/
unsigned char fs_fputcluster(unsigned char* _pClu, struct fs_descrip *fs_descrip);

/****************************************************************
char fs_fgets(char *s, int n, struct fs_descrip *fs_descrip);

DESCRIPTION:
	Returns a null-terminated string of characters from the opened file
   'fs_descrip', of up to n characters.  Returns when a newline '\n', or end of
   file is reached.

RETURNS:
	The contents of fs_descrip->ff_status (fOK if all is correct).
*****************************************************************/
char fs_fgets(char *s, int n, struct fs_descrip *fs_descrip);

/****************************************************************
char fs_fprintf(struct fs_descrip *fs_descrip,const char *format,...)

DESCRIPTION:
	Similar to 'printf', writing a formatted string to the opened file pointed
   by fs_descrip.

RETURNS:
	The number of characters successfully output.

NOTES:
   Automatically adds carriage return '\r' before newline '\n', as in standard
   DOS practice.  If you need to work with binary data, you should use fputc
   and fgetc directly.
*****************************************************************/
int fs_fprintf(struct fs_descrip *fs_descrip,const char *format,...);

/****************************************************************
char fs_fclose(struct fs_descrip *fs_descrip)

DESCRIPTION:
	Closes a file previously opened with fs_fopen, saving any lingering changes,
   updating directory entries, and freeing memory associated with fs_descrip
   (be sure to only fs_fclose file handles created with fs_fopen, and not
   something like fs_findnext).

RETURNS:
	Returns error code associated with file; the contents of fs_descrip are
   no longer valid after this call, do not check its ff_status field.
*****************************************************************/
int fs_fclose(struct fs_descrip *fs_descrip);

/****************************************************************
char fs_fflush(struct fs_descrip *fs_descrip)

DESCRIPTION:
	Flushes changes made to a file to disk in its current form.  Use this
   to write your current modifications to disk, and also update the file
   directory.  Even if your controller is powered off at this point, or
   your card is removed, the contents of the file would be saved.

RETURNS:
	Returns error code associated with file, or fILLEGAL if file not open
   for writing and modification.
*****************************************************************/
int fs_fflush(struct fs_descrip *fs_descrip);

/****************************************************************
char fs_fseek(struct fs_descrip *fs_descrip, long index)

DESCRIPTION:
	Sets the current read/write point in an already opened file to 'index'.

   Subsequent calls to calls like fs_fputc()/fs_fgetc() will occur at the
   newly specified index.

	The index is given as an absolute position in the filesize.

   - 0:              position set to beginning of file.
   - 0xffffffff:     (or any value larger than the filesize), position set to
                     end of file, so that the next fgetc() would return EOF.)

RETURNS:
	Returns error code associated with file.
*****************************************************************/
int fs_fseek(struct fs_descrip *fs_descrip, unsigned long index);

/****************************************************************
int fs_fremove(struct fs_descrip *fs_descrip)

DESCRIPTION:
   Remove/delete a file from the filesystem.  The file must first be opened
   with fs_fopen() before it is passed into tihs argument.  The memory for the
   file descriptor will be freed upon returning (as if fclose() was called).

RETURNS:
	Returns error code associated with file; the contents of fs_descrip are
   no longer valid after this call, do not check its ff_status field.
*****************************************************************/
int fs_fremove(struct fs_descrip *fs_descrip);

/****************************************************************
void fs_StampTimeHMSMDY(struct fs_descrip *fs_descrip,char TDtype,
		unsigned int hour, unsigned int min,unsigned int sec,
		unsigned int month, unsigned int day, unsigned int year);

DESCRIPTION:
	Changes the time stamp for either file 'access', 'modification', or
   'creation' for a file pointed to by fs_descrip in the directory entry.
   The 'year' argument should be the full year (ie, 2001).

   Since not all systems have RTC, the user is expected to use this function
   if they wish to use file timestamps. fs_fopen, fs_fclose, etc... will not.

   DOS usually stores timestamps in 'packed' storage format (documentation
   available online).
*****************************************************************/
void fs_StampTimeHMSMDY(struct fs_descrip *fs_descrip,char TDtype,
		unsigned int hour, unsigned int min,unsigned int sec,
		unsigned int month, unsigned int day, unsigned int year);

/****************************************************************
void fs_frdump(struct fs_descrip *fs_descrip,unsigned char far* _dest,
	unsigned long _length);

DESCRIPTION:
   Support quicker reads from the file by dumping directly into a specified
   buffer address.  Be very careful passing in 'far' _dest; only supported
   on limited platforms (SLC) at this point.

   The position of the file is set to the end of the dumped data if successful.

RETURN VALUE:
	The number of bytes successfully read from the file.  If this is
   less than the argument '_length', check the files ff_status for possible
   errors.
*****************************************************************/
unsigned long fs_frdump(struct fs_descrip *fs_descrip, unsigned char far* _dest,
	unsigned long _length);

/****************************************************************
void fs_fwdump(struct fs_descrip *fs_descrip,unsigned char far* _src,
	unsigned long _length);

DESCRIPTION:
   Support quicker writes to filesystem by eliminating double-buffering,
   and DMA writes directly from buffer into filesystem.  Don't assume that
   all contents of buffer have been written to disk; some might be backed
   up in SRAM as usual, and a fs_fclose()/fs_fflush() will be needed to
   force a write to disk.

   The position of the file is set to the end of the dumped data.

RETURN VALUE:
	The number of bytes successfully written to the card.  If this is
   less than the argument '_length', check the files ff_status for possible
   errors.

NOTE:
	Consider using fs_callocate and dma_wr_cf() (from mma.h) for even
   faster performing writes to card.
*****************************************************************/
unsigned long fs_fwdump(struct fs_descrip *fs_descrip, unsigned char far* _src,
	unsigned long _length);

/****************************************************************
unsigned long fs_XlateToSector(unsigned int clusteraddr);

DESCRIPTION:
This is a helper function, used to translate a cluster address in the
filesystem into an actual sector address that can be used for raw read/writes.
The argument is a cluster address (usually the ff_current field of an
open file), and the return value is a sector address.

See the contents of mma.h for functions available for raw access to storage.
(Usually dma_wr_cf(), and dma_rd_cf()).
*****************************************************************/
unsigned long fs_XlateToSector(unsigned int clusteraddr);

/****************************************************************
unsigned char fs_callocate(struct fs_descrip *_file, unsigned int _length);

DESCRIPTION:
This function helps you allocate an amount of memory in your file, in the
form of a contiguous block of sectors.  The advantage of pre-allocating your
file this way is that you can then use raw memory accesses (see mma.h) to
more efficiently and quickly access stored data.  The performance improvements
from using this function can be as much as 10-times better than normal
filesystem accesses.

The _length argument is the amount of memory to pre-allocate in terms of
number of *CLUSTERS* (not bytes).  If the current "end" cluster of the
_file is also empty, that cluster *may* be used (if a set of continuous
clusters can be found behind it).  Otherwise, a new cluster will be
found.  This means some random data might exist between the end of
the file before this cal, and the beginning of the callocated clusters.

Upon a successful call, all clusters are assigned to this file, and the
file size is adjusted accordingly.  The position is set to the beginning
of the continuously allocated clusters.

After the call finishes, an extra cluster (not necessarily continuous)
is also assigned to the file to meet required file semantics (but file
size only reflects the additional clusters).

RETURN VALUE:
The value of _file->ff_status.
*****************************************************************/
unsigned char fs_callocate(struct fs_descrip *_file, unsigned int _length);

/****************************************************************
struct fs_descrip *fs_fopen_callocate(const char *filename,
                                      unsigned int clusters_allocated);

A combination of the effects of fs_fopen and fs_callocate(), this
function creates a new file at a location where 'clusters_allocated'
contiguous clusters can be found.  There are no flags to be sent; they
are assumed to be 'O_WRITE'.

RETURN VALUE:
Return value similar to those from default 'fs_fopen' call, and the
value of 'ff_status' must be checked to determine success of the
operation.

If not enough available clusters can be located, 'fDISKFULL' will be returned
as the ff_status error value.
*****************************************************************/
struct fs_descrip *fs_fopen_callocate(const char *filename,
                                      unsigned int clusters_allocated);

#ifndef TERN_16_BIT
/*******************************************************************
The fs_setCFAddress() function is available to set the hardware address
of the CompactFlash card that is to be accessed through the filesystem.
This should only be called once, before the initial setup call to
fs_initPCFlash().

The default address (for standard FlashCore-B, or FlashCore-0, etc.) is
0xe0.  Other addresses supported at this time include:
   0x80, 0xd0, 0xf0.

For details, see mm8.h.  (16-bit CompactFlash is not available as
multiple expansion at this time.)

NOTE: Available for 186-based systems only at this time.
********************************************************************/
void fs_setCFAddress(unsigned char address);
#endif

/*******************************************************************
A fs_descrip structure is used as a file handle to an open file.  The
structure might change over time, and you should be careful in accessing
any fields directly.

A fs_descrip variable might be created in two ways:

1) created on the heap via a call to fs_fopen(); this must later be freed
by a corresponding call to fs_fclose(), even if an error occured at some
point with the file.

2) a call to fs_findfirst(), passing in a fs_descrip variable you've created
(in any way).  The file isn't actually open, and you are responsible for
freeing the variable.
********************************************************************/
struct fs_descrip {
  unsigned int  ff_dirpos;  // The number of the directory entry for this file.
  unsigned int  ff_start,   // The starting cluster.
		ff_current;  // The cluster currently being written to.
  char ff_attrib;       // Attribute byte, see FA_xxx above.
  char ff_mode;		// Either fREAD or fWRITE or fCLOSED.
  unsigned int  ff_ctime, ff_cdate;        // File created time and date.
  unsigned int  ff_mtime, ff_mdate;        // File modified time and date.
  unsigned int  ff_adate;        // File accessed date, no time stored.
  unsigned long ff_fsize,        // File size in bytes.
		ff_position;  // The 'read' pointer.
  int ff_status;		// For passing error information.
  char ff_name[FNLEN+1];  // File name.
  unsigned char *ff_buf;  // Cluster buffer, sectors must be read and written
  unsigned char ff_touched; // Flag indicating whether current buffer changed...

  unsigned int  ff_DirCluster; // Directory cluster for this file.
};

#endif /* _FILEIO_H_ */
